package com.withouther.robot.rainingrobot.util.ai.asr;

import cn.hutool.core.convert.Convert;
import com.alibaba.nls.client.AccessToken;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import com.withouther.robot.rainingrobot.util.StaticParam;
import com.withouther.robot.rainingrobot.util.ai.asr.listener.impl.SinowelAsrListener;
import com.withouther.robot.rainingrobot.util.ai.asr.model.MessageChange;
import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("all")
@Slf4j
public class SpeechTranscriberUtil {

	private String appKey;
	
	private String accessToken;
	// private static SpeechTranscriberUtil demo;
	private SpeechTranscriber transcriber;

	private SinowelAsrListener sinowelAsrListener;

	private Map<String, Object> parameters = new HashMap<>();
	
	NlsClient client;

	public SpeechTranscriberUtil(String appKey, String token, Map<String, Object> parameters) {
		this(appKey, token);
		this.parameters = parameters;
	}
	public SpeechTranscriberUtil(String appKey, String token) {
		this.appKey = appKey;
		this.accessToken = token;
		// Step0 创建NlsClient实例,应用全局创建一个即可,默认服务地址为阿里云线上服务地址
		client = new NlsClient(accessToken);
	}
	public SpeechTranscriberUtil(String appKey, String id, String secret, String url, Map<String, Object> parameters) {
		this(appKey, id, secret, url);
		this.parameters = parameters;
	}

	public SpeechTranscriberUtil(String appKey, String id, String secret, String url) {
		this.appKey = appKey;
		//TODO 重要提示 创建NlsClient实例,应用全局创建一个即可,生命周期可和整个应用保持一致,默认服务地址为阿里云线上服务地址
		//TODO 这里简单演示了获取token 的代码，该token会过期，实际使用时注意在accessToken.getExpireTime()过期前再次获取token
		AccessToken accessToken = new AccessToken(id, secret);
		try {
			accessToken.apply();
			System.out.println("get token: " + ", expire time: " + accessToken.getExpireTime());
			// TODO 创建NlsClient实例,应用全局创建一个即可,用户指定服务地址
			if(url.isEmpty()) {
				client = new NlsClient(accessToken.getToken());
			}else {
				client = new NlsClient(url, accessToken.getToken());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private SpeechTranscriberListener getTranscriberListener(SinowelAsrListener sinowelAsrListener) {
		SpeechTranscriberListener listener = new SpeechTranscriberListener() {
			// 识别出中间结果.服务端识别出一个字或词时会返回此消息.仅当setEnableIntermediateResult(true)时,才会有此类消息返回
			@Override
			public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
				System.out.println("name: " + response.getName() +
				// 状态码 20000000 表示正常识别
				", status: " + response.getStatus() +
				// 句子编号，从1开始递增
				", index: " + response.getTransSentenceIndex() +
				// 当前的识别结果
				", result: " + response.getTransSentenceText() +
				// 当前已处理的音频时长，单位是毫秒
				", time: " + response.getTransSentenceTime());
				MessageChange messageChange = new MessageChange();
				messageChange.setMessage(response.getTransSentenceText());
				messageChange.setTime(response.getTransSentenceTime());
				messageChange.setVad(1);

				sinowelAsrListener.onMessageChange(messageChange);
			}

			// 识别出一句话.服务端会智能断句,当识别到一句话结束时会返回此消息
			@Override
			public void onSentenceEnd(SpeechTranscriberResponse response) {
				/*AudioAsrServiceImpl
				 * String taskID = transcriber.getTaskId();
				 * System.out.println("taskID: " + taskID);
				 */
				String uid = (String) transcriber.context.get("contextID");
				/*
				 * System.out.println("name: " + response.getName() + // 状态码
				 * 20000000 表示正常识别 ", status: " + response.getStatus() + //
				 * 句子编号，从1开始递增 ", index: " + response.getTransSentenceIndex() +
				 * // 当前的识别结果 ", result: " + response.getTransSentenceText() +
				 * // 当前已处理的音频时长，单位是毫秒 ", time: " +
				 * response.getTransSentenceTime());
				 */
				String text = response.getTransSentenceText();
				sinowelAsrListener.onMessage(text);
			}

			// 识别完毕
			@Override
			public void onTranscriptionComplete(SpeechTranscriberResponse response) {
				System.out.println("name: " + response.getName() + ", status: " + response.getStatus());
			}

			@Override
			public void onTranscriberStart(SpeechTranscriberResponse response) {
				// System.out.println("task_id: " + response.getTaskId() + ",
				// name: " + response.getName() + ", status: " +
				// response.getStatus());
			}

			@Override
			public void onSentenceBegin(SpeechTranscriberResponse response) {
				// System.out.println("task_id: " + response.getTaskId() + ",
				// name: " + response.getName() + ", status: " +
				// response.getStatus());
			}

			@Override
			public void onFail(SpeechTranscriberResponse response) {
				// TODO 重要提示：
				// task_id很重要，是调用方和服务端通信的唯一ID标识，当遇到问题时，需要提供此task_id以便排查
				System.out.println("task_id: " + response.getTaskId() + ", status: " + response.getStatus()
						+ ", status_text: " + response.getStatusText());

			}
		};
		return listener;
	}

	public void process(FileInputStream ins, String uid, SinowelAsrListener sinowelAsrListener) {
		
		try {
			//System.out.println("1: put uid into contextID : " + uid);
			transcriber = new SpeechTranscriber(client, getTranscriberListener(sinowelAsrListener));
			transcriber.setAppKey(appKey);
			// 输入音频编码方式
			transcriber.setFormat(InputFormatEnum.PCM);
			// 从配置文件获取 输入音频采样率
			int sampleRate = Convert.toInt(parameters.get("sampleRate"), 16000);
			if (SampleRateEnum.SAMPLE_RATE_8K.value == sampleRate) {
				transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_8K);
			} else {
				transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
			}
			// 是否返回中间识别结果
			transcriber.setEnableIntermediateResult(true);
			// 是否生成并返回标点符号
			transcriber.setEnablePunctuation(true);
			// 是否将返回结果规整化,比如将一百返回为100
			transcriber.setEnableITN(false);
//			设置vad断句参数，默认800ms，有效值[200, 2000]
			transcriber.addCustomedParam("max_sentence_silence", 700);
//			是否开启顺滑
//			transcriber.addCustomedParam("disfluency",true);
			transcriber.addCustomedParam("speech_noise_threshold",0.3);
//			transcriber.addCustomedParam("vad_model","farfield");
			transcriber.putContext("contextID", uid);
			//System.out.println("2: put uid into contextID : " + uid);
			// Step2 此方法将以上参数设置序列化为json发送给服务端,并等待服务端确认
			transcriber.start();
			// Step3 语音数据来自声音文件用此方法,控制发送速率;若语音来自实时录音,不需控制发送速率直接调用
			// recognizer.sent(ins)即可
			// 从配置文件获取 阿里传输字节大小（byte）
			// 从配置文件获取 阿里上传流的间隔时间（ms）
			// 4096
			int byteLength = Convert.toInt(parameters.get("byteLength"), 2048);
			log.info("阿里,采样率为:" + sampleRate + ",输入的字节为：" + byteLength);
			asr(ins, byteLength, getSleepDelta(byteLength, sampleRate) - 80, uid);
			// transcriber.send(ins,Integer.parseInt(ali_batchSize),Integer.parseInt(ali_sleepInterval));
			transcriber.stop();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// Step5 关闭连接
			if (null != transcriber) {
				transcriber.close();
			}
		}
	}
	private void asr(FileInputStream stream, int ali_batchSize, int ali_sleepInterval, String uid) throws Exception {
        try {
            int n = 0, avaiable = 0;
			log.info("需要判断的channelId为:" + StaticParam.routeNodeMap.get(transcriber.context.get("contextID")));
			do {
                try {
                    Thread.sleep(ali_sleepInterval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                byte[] bytes = new byte[ali_batchSize];
				avaiable = stream.available();
				if (avaiable < ali_batchSize) {
					continue;
				}
                n = stream.read(bytes);
				if (!transcriber.getConnection().isActive()) {
					System.err.println("当前语音识别是不活跃的，请检查");
					break;
				}
				transcriber.send(bytes);

            } while (null != StaticParam.routeNodeMap.get(transcriber.context.get("contextID")));
            log.info("阿里：结束实时语音。 remove context uid:" + transcriber.context.remove("contextID"));
            //System.out.println("阿里：结束实时语音。");
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
	/// 根据二进制数据大小计算对应的同等语音长度
	/// sampleRate 仅支持8000或16000
	public static int getSleepDelta(int dataSize, int sampleRate) {
		// 仅支持16位采样
		int sampleBytes = 16;
		// 仅支持单通道
		int soundChannel = 1;
		return (dataSize * 10 * 8000) / (160 * sampleRate);
	}
	public void shutdown() {
		client.shutdown();
	}

	@SuppressWarnings("unused")
	public void start(String uid, String file, SinowelAsrListener sinowelAsrListener) throws Exception {
		FileInputStream ins = new FileInputStream(file);
		if (null == ins) {
			System.err.println("open the audio file failed!");
			return;
		}
		StaticParam.routeNodeMap.put(uid, "on");
		process(ins, uid, sinowelAsrListener);
	}
}
